package gam.n1.areas.a;


import com.google.common.collect.Maps;
import gam.n1.achievement.AchievementManager;
import gam.n1.achievement.iAchievementManager;
import gam.n1.areas.Area;
import gam.n1.areas.AreaManager;
import gam.n1.areas.block.iBox;
import gam.n1.areas.impl.a.DefaultDefendVArea;
import gam.n1.ask.AskYard;
import gam.n1.be.Behavioral;
import gam.n1.conf.ConfManager;
import gam.n1.ins.InsCreator;
import gam.n1.ins.Instance;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;

public abstract class BaseBukkitAreaCustom implements Area {
    private final Plugin plugin;
    private String aN;
    private String dN;
    protected Listener be;
    private UUID cu;
    protected Queue<Instance> ins; //条件行为
    protected iBox box;
    public Random random;
    private List<Behavioral> bl; //默认行为
    protected List<Entity> es;
    protected InsCreator ic;
    protected iAchievementManager am;
    private Instance currentInstance;
    protected long currentTime;

    private final AtomicBoolean go;

    public BaseBukkitAreaCustom(@NotNull Plugin plugin, @NotNull String key){
        this(plugin,key,null);
    }

    public BaseBukkitAreaCustom(@NotNull Plugin plugin, @NotNull String key, iBox box){
        this(plugin,key,null,box);
    }
    public BaseBukkitAreaCustom(@NotNull Plugin plugin, @NotNull String key, String displayName, iBox box) {
        this.cu = UUID.randomUUID();
        this.random = new Random();
        this.aN = key;
        this.dN = displayName;
        this.go = new AtomicBoolean(false);
        this.am = new AchievementManager();
        this.box = box;
        this.plugin = plugin;
    }
    @Override
    public void refresh() {
        for (Chunk var1 : box.cs()) {
            es = Arrays.stream(var1.getEntities()).filter(e -> !e.isDead() && box.ie(e)).collect(Collectors.toList());
        }
    }
    @Override
    public boolean beforeAction() {
        if (es == null) {
            es = Collections.synchronizedList(new ArrayList<>());
        }
        return true;
    }

    @Override
    public void afterAction() {
        this.currentTime = System.currentTimeMillis();
    }
    protected List<Player> players(){
        return getEntitys().stream().filter(e -> e instanceof HumanEntity).map(e -> (Player) e).collect(Collectors.toList());
    }

    @Override
    public boolean cI() {
        return ins != null && bl != null && ins.size() > 0 && bl.size() > 0;
    }

    @Override
    public boolean cD() {
        return false;
    }

    @Override
    public iAchievementManager getAchievementManager() {
        return am;
    }

    @Override
    public boolean join(Player player) {
        if (!isRunning() && player.isOnline()){
            getAchievementManager().initUUID(player.getUniqueId().toString());
            refresh();
            return true;
        }
        return false;
    }

    @Override
    public void leave(Player player) {
        getAchievementManager().deleteOne(player.getUniqueId().toString());
        refresh();
    }

    @Override
    public Plugin plugin() {
        return plugin;
    }
    @Override
    public void tick() {
        if (!go.get() && beforeAction()) {
            begin();
            System.out.println("--1");
            afterAction();
        }
        if (go.get()){
            System.out.println("--2");
            // 首先执行默认行为
            if (bl.size() > 0) bl.forEach(_b -> _b.accept(this));
            // 其次执行条件行为
            if (this.currentInstance != null){
                System.out.println("--5");
                if (this.currentInstance.apply(this)){
                    this.currentInstance = null;
                }
            } else {
                System.out.println("--4");
                System.out.println(ins.size());
                if (ins.size() > 0 && ne()){
                    this.currentInstance = ins.poll();

                }
            }
            if (beforeEnd()) {
                die();
                afterEnd();
                System.out.println("done");
            }
        }
    }
    protected boolean ne(){
        return true;
    }

    @Override
    public boolean beforeEnd() {
        return ins.size() == 0 && this.currentInstance == null;
    }
    @Override
    public void afterEnd() {
        this.currentTime = 0;
        rS();
    }
    protected void rS(){
    }

    @Override
    public boolean playerInGame(Player player) {
        return players().stream().anyMatch(p -> p.getUniqueId().equals(player.getUniqueId()));
    }
    private void begin(){
        this.go.compareAndSet(false,true);
    }
    private void die(){
        this.go.compareAndSet(true,false);
    }

    @Override
    public boolean isEntityInArea(Entity entity) {
        return box.ie(entity);
    }

    @Override
    public iBox getBox() {
        return box;
    }

    @Override
    public void setBox(iBox box) {
        if (this.box == null && !isRunning()){
            this.box = box;
        }
    }

    @Override
    public Set<Chunk> getChunksInArea() {
        return box.cs();
    }
    @Override
    public World getAreaInWorld() {
        return box.w();
    }
    @Override
    public Player getCreator() {
        return Bukkit.getPlayer(this.cu);
    }
    @Override
    public String getAreaName() {
        return aN;
    }
    @Override
    public UUID getAreaUUID() {
        return cu;
    }
    @Override
    public String getAreaDisplayName() {
        return dN;
    }
    @Override
    public Area instances(List<Instance> ins) throws DefaultDefendVArea.CanNotModifiedException {
        if (!isRunning()){
            this.ins = new LinkedBlockingQueue<>(ins);
            return this;
        }else throw new DefaultDefendVArea.CanNotModifiedException("The game is running and cannot be modified!");
    }
    @Override
    public Area behaviorals(List<Behavioral> bl) throws Exception {
        if (!isRunning()){
            if (this.bl != null){
                this.bl.addAll(bl);
            } else this.bl = bl;
            return this;
        } else throw new DefaultDefendVArea.CanNotModifiedException("The game is running and cannot be modified!");
    }

    @Override
    public List<Entity> getEntitys() {
        return Collections.unmodifiableList(es);
    }

    @Override
    public boolean isRunning() {
        return go.get();
    }

    @Override
    public long currentTime() {
        return currentTime;
    }

    @Override
    public InsCreator getInsCreator() {
        return ic;
    }

    @Override
    public Random random() {
        return random;
    }

    @Override
    public CompletableFuture<Area> aH(Player p) {
        return CompletableFuture.supplyAsync(()->gP(p).apply(this)).thenApplyAsync(gP(p));
    }
    private Function<Area,Area> gP(Player z) {
        return area -> {
            ConfManager.Msg.send(z,ConfManager.Msg.getString("area.create.set.point"),null,null);
            AskYard p = new AskYard(area);
            Bukkit.getPluginManager().registerEvents(p,area.plugin());
            return p.get();
        };
    }

    @Override
    public Map<String,Object> save() {
        Map<String,Object> saveMap = Maps.newHashMap();
        saveMap.put("type",AreaManager.hasRegister(this.getClass()));
        saveMap.put("name",getAreaName());
        saveMap.put("displayName",getAreaDisplayName() == null ? "" : getAreaDisplayName());
        saveMap.put("box",box.save());
        return saveMap;
    }

    @Override
    public void setAreaUUID(UUID uuid) {
        this.cu = uuid;
    }

    @Override
    public void setAreaDisplayName(String name) {
        this.dN = name;
    }

    @Override
    public void setAreaName(String name) {
        this.aN = name;
    }

}
